#ifndef _TABLE_CONSTRAINEDATTRIBUTE_DESC_H_
#define _TABLE_CONSTRAINEDATTRIBUTE_DESC_H_

#include <buildspec.h>
#include <GSTenums.h>
#include <exceptions/GSTRuntimeException.h>
#include <Utils/Converter.h>
#include <Client/ClientUtils/Network/TableAttributeDesc.h>
#include <Client/ClientUtils/Network/SimplexPropertyDesc.h>
#include <Client/ClientUtils/Network/ObjectPropertyDesc.h>
#include "Client/ClientUtils/Network/TableAttributeVal.h"
#include <string>
#include <map>

#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/archive/xml_oarchive.hpp> 
#include <boost/archive/xml_iarchive.hpp>

#include <Geometry/boost_serialize_includes.hpp>

namespace GST {
namespace ClientUtils {

	class ForeignKey;

/**
  * A relation constructing a foreign key Item. 
  *
  * \li targetColumn: A table column referencing a primary key column of a third table (targetColumn)
  * \li sourceColumn: A primary key column of a third party table (keyColumn) that is referenced by the pair::first value
  */  
class GST_API_EXPORT ForeignKeyItem
{
	friend class boost::serialization::access;

public:
	typedef int CPLinkId;
private:
	TableAttributeDescPtr targetColumn;
	TableAttributeKeyDescPtr sourceColumn;
	CPLinkId constrained_property_table_id;
public:
	ForeignKeyItem();

	ForeignKeyItem(TableAttributeDescPtr _targetColumn, TableAttributeKeyDescPtr _sourceColumn);

	/**
	 * constructs a persistent foreign key
	 *
	 *		@parameter _targetColumn                    description for column in the feature class table, that corresponds to key column in the source table
	 *		@parameter _sourceColumn                    description of primary key column in the source table
	 *		@parameter _constrained_property_table_id   value of id in gst.constrained_properties_link table  from the item describing 
	 *										            relationship between target column and key column
	 */
	ForeignKeyItem(TableAttributeDescPtr _targetColumn, TableAttributeKeyDescPtr _sourceColumn, int _constrained_property_table_id);
	ForeignKeyItem(const ForeignKeyItem& other);
	TableAttributeDescPtr getTargetColumn() const;
	TableAttributeKeyDescPtr getSourceColumn() const;
	std::string getTargetKeyColumnName() const;
	std::string getSourceKeyColumnName() const;
	std::string getSourceKeyColumnTableName() const;
	int getCPLinkId() const;
	bool isPersistent() const;
	void changeDatabaseName( const std::string & dbname );

	bool operator==( const ForeignKeyItem & other) const;
private:
	template <typename Archive> 
	void serialize(Archive &ar, const unsigned int version) 
	{ 
		//If you change members here do not forget to increment object version (see bottom of this file)
		ar & boost::serialization::make_nvp("target_column"    ,this->targetColumn);
		ar & boost::serialization::make_nvp("source_column"   ,this->sourceColumn); 
	}
};
typedef boost::shared_ptr<ForeignKeyItem> ForeignKeyItemPtr;

/**
  * A list of ForeignkeyItems constructing a foreign key. 
  *
  */

class GST_API_EXPORT ForeignKey
{
	friend class boost::serialization::access; 
public:
    typedef enum FKTYPE{NOACTION=0, RESTRICT=1, SETNULL=2} FKTYPE;
private:
	std::vector<ForeignKeyItemPtr> foreignKey;
	FKTYPE fk_type;

public:
	ForeignKey();
	ForeignKey(FKTYPE _fk_type);
	ForeignKey(const ForeignKey& other);
	~ForeignKey();

	bool isPersistent() const;
	bool targetIsNotNULL() const;
	bool operator==( const ForeignKey & other) const;
	void AddForeignKeyItem(ForeignKeyItemPtr key);
	void AddForeignKeyItem(TableAttributeDescPtr targetColumn, TableAttributeKeyDescPtr keyColumn);
	std::string getSourceTableName() const;
	std::string getTargetKeyColumnName(int keyPosition) const;
	std::string getSourceKeyColumnName(int keyPosition) const;
	int getSize() const;
	FKTYPE getFKType() const;
	std::string getFKTypeAsString() const;
	std::vector<ForeignKeyItem::CPLinkId> getPersistentTableIds() const;
	TableAttributeDescPtr getTargetColumn(int keyPosition) const;
	ForeignKeyItemPtr getTargetColumn(const TableAttributeDesc & pkComponent) const;
	TableAttributeKeyDescPtr getSourceColumn(int keyPosition) const;
	std::string GlueTargetColumnNames(const std::string & glue=std::string(",")) const;
	std::string GlueSourceColumnNames(const std::string & glue=std::string(",")) const;
	void setFKType(FKTYPE _fktype);
	/**
	  *	Replaces the database name in the foreign key component
	  */
	void changeDatabaseName( const std::string & dbname );
	void reorderForeignKey(TableAttributeKeyDescListPtr rightorder);

private:
	template <typename Archive> 
	void serialize(Archive &ar, const unsigned int version) 
	{ 
		//If you change members here do not forget to increment object version (see bottom of this file)
		ar & boost::serialization::make_nvp("fk_type"       ,this->fk_type); 
		ar & boost::serialization::make_nvp("foreignKey"    ,this->foreignKey);
	}
};

typedef boost::shared_ptr<ForeignKey> ForeignKeyPtr;

//compare operator at the bottom

/**
  * This is the basis class for the constrained property pattern in GST.
  * 
  * The source_map_column presents the referenced column of a third party table. This column is mapped
  * to the feature class by the foreign keys of the referenced table. The derived classes ConstrainedSimplexPropertyDesc and ConstrainedObjectPropertyDesc
  * presents the column in the feature class (="constrained column" in GST Desktop).
  *
  * The source_map_column can be also a column of a view in order
  * to hide the complexity of third party models by using views like prepared table joins, prepared  filters (where clause) etc. 
  * To reference a view (instead of a table) set the source_view_name to the referenced view (see setSourceViewName()). 
  * If source_view_name is empty (default) the source_map_column refers to a table. If source_view_name refers to an existing view,
  * source_map_column refers to a column in the view.

  * The limitation to the view is:
  *		\li The view must have <b>the complete primary key</b> of the referenced table <b>without using aliases for the primary keys</b> (so all primary key 
  *			columns of the referenced table have to occur with the same name in the view)
  *
  * The source_key_columns is the foreign key used to reference the third party table (so it also represents the primary keys of the third party table). 
  * Use AddKeyColumn() to build up the foreign key.
  * The source_key_columns should be built up by columns being unique for each record in the referenced table or the referenced view. (Being the primary key 
  * or having a unique constraint by the database is not required but is suggested.)
  *
  * fk_type represent the constrains handled by the database on delete of the records in the referenced table (default: NOACTION).
  *	Please refer to http://en.wikipedia.org/wiki/Foreign_key#RESTRICT ff.	
  *
  * @see ConstrainedObjectPropertyDesc
  * @see ConstrainedSimplexPropertyDesc
  * @see ConstrainedTableAttributeVal
  */
class GST_API_EXPORT TableConstrainedAttributeDesc
{
	friend class boost::serialization::access; 

public:
	TableConstrainedAttributeDesc(void);
	TableConstrainedAttributeDesc(const TableConstrainedAttributeDesc & other);
	TableConstrainedAttributeDesc(TableAttributeDescListPtr map_columns, const ForeignKey::FKTYPE fk_type);
	TableConstrainedAttributeDesc(TableAttributeDescListPtr map_columns, const ForeignKey::FKTYPE fk_type, const std::string& viewname);
	virtual ~TableConstrainedAttributeDesc(void);

	std::string GetSourceColumnTableName() const;
	std::vector<std::string> GetSourceColumnNames() const;
	// TODO: the one below is a bit tricky, I am not sure if we can change the key of TableAttributeDescListPtr easily
	void setSourceColumnName(const std::string & name);
	TableAttributeDescListPtr GetSourceColumns() const;
	
	ForeignKeyPtr GetForeignKey() const;
	void SetForeignKey(ForeignKeyPtr fk);
	///add foreign key item
	void AddForeignKeyColumn(TableAttributeDescPtr targetColumn, TableAttributeKeyDescPtr keyColumn);
	void AddForeignKeyColumn(ForeignKeyItemPtr fk);
	void setSourceViewName(std::string viewname);
	std::string GetSourceViewName()const ;
	std::string GetSourceTableName() const;

	ForeignKey::FKTYPE GetFKType() const;
	void SetFKType(ForeignKey::FKTYPE fktype);
	virtual bool operator==(const TableConstrainedAttributeDesc &other) const;

	virtual void changeDatabaseName( const std::string & dbname );

private:
	///name serialized members
	//@{
	TableAttributeDescListPtr source_map_columns;
	ForeignKeyPtr foreign_key;
	/**	if TableConstrainedAttributeDesc is persistent this vector is sized like source_key_columns associating 
	  * a ForeignKey part to a record in table gst.constrained_properties_link  */
//	std::vector<CPLinkId> constrained_property_table_ids;
	std::string source_view_name;
	//@}

	template <typename Archive> 
	void serialize(Archive &ar, const unsigned int version) 
	{ 
		//If you change members here do not forget to increment object version (see bottom of this file)
		ar & boost::serialization::make_nvp("source_view_name"    ,this->source_view_name);
		ar & boost::serialization::make_nvp("source_map_columns"   ,this->source_map_columns); 
		if(version<3) {
			throw exceptions::GSTRuntimeException(__METHOD_NAME__, "Can not read template below version 3.");
		}
		ar & boost::serialization::make_nvp("foreign_key"  ,this->foreign_key);
	}

};	
typedef boost::shared_ptr<TableConstrainedAttributeDesc> TableConstrainedAttributeDescPtr;

//---------------------------------------------------------------------------------------
/**
  * This is a TableConstrainedAttributeDesc presenting the mapping of a third party column to the feature simplex property.
  * This column is also a non real (virtual) column represented by SimplexPropertyDesc, where getName() holds the displayed name in 
  * feature class and getTableName() the feature classes name. The left members (dtype, dimension, notNull) are equal to the
  * source_map_column this property is representing.
  */
class GST_API_EXPORT ConstrainedSimplexPropertyDesc
	: public TableConstrainedAttributeDesc
	, public SimplexPropertyDesc
{
	friend class boost::serialization::access; 

public:
	ConstrainedSimplexPropertyDesc(void)
		: SimplexPropertyDesc()
		, TableConstrainedAttributeDesc()
	{}
	ConstrainedSimplexPropertyDesc(const SimplexPropertyDesc & spxProp, const TableConstrainedAttributeDesc & cp)
		: SimplexPropertyDesc(spxProp)
		, TableConstrainedAttributeDesc(cp)
	{}
	ConstrainedSimplexPropertyDesc(const std::string& name, const std::string& tablename, DType type, int dimension, bool notNull, Geometry::PropertyAlignments alignment, TableAttributeDescListPtr map_columns, ForeignKey::FKTYPE fk_type=ForeignKey::NOACTION,const std::string& viewname="", const std::string& alternativeType="")
		: SimplexPropertyDesc(name,tablename,type,dimension,notNull,alignment,alternativeType)
		, TableConstrainedAttributeDesc(map_columns,fk_type,viewname)
	{}	
	~ConstrainedSimplexPropertyDesc(void)
	{

	}
	virtual bool operator ==(const ConstrainedSimplexPropertyDesc& other) const
	{
		return (SimplexPropertyDesc::operator==(other) && TableConstrainedAttributeDesc::operator==(other));
	}
	virtual bool isConstrained() const;

private:
	template <typename Archive> 
	void serialize(Archive &ar, const unsigned int version) 
	{ 
		//If you change members here do not forget to increment object version (see bottom of this file)
		ar & boost::serialization::make_nvp("SimplexPropertyDesc", boost::serialization::base_object<SimplexPropertyDesc>(*this) ); 
		ar & boost::serialization::make_nvp("ConstrainedSimplexProperty", boost::serialization::base_object<TableConstrainedAttributeDesc>(*this) ); 
	}

};
typedef boost::shared_ptr<ConstrainedSimplexPropertyDesc> ConstrainedSimplexPropertyDescPtr;


//---------------------------------------------------------------------------------------
/**
  * This is a TableConstrainedAttributeDesc presenting the mapping of a third party column to the feature object property.
  * This column is also a non real (virtual) column represented by ObjectPropertyDesc, where getName() holds the displayed name in 
  * feature class and getTableName() the feature classes name. The left members (dtype, dimension, notNull) are equal to the
  * source_map_column this property is representing.
  */
class GST_API_EXPORT ConstrainedObjectPropertyDesc
	: public TableConstrainedAttributeDesc
	, public ObjectPropertyDesc
{
	friend class boost::serialization::access; 

public:
	ConstrainedObjectPropertyDesc(void)
		: TableConstrainedAttributeDesc()
		, ObjectPropertyDesc()
	{}
	ConstrainedObjectPropertyDesc(const ObjectPropertyDesc & objProp, const TableConstrainedAttributeDesc & cp)
		: ObjectPropertyDesc(objProp)
		, TableConstrainedAttributeDesc(cp)
	{}
	ConstrainedObjectPropertyDesc(const ConstrainedObjectPropertyDesc & other)
		: ObjectPropertyDesc(other.getName(),other.getTableName(),other.getType(),other.getDimension(),other.isNotNull(),other.getAlternativeType())
		, TableConstrainedAttributeDesc(other)
	{
	}
	ConstrainedObjectPropertyDesc(const std::string& name, const std::string& tablename, DType type, int dimension, bool notNull, TableAttributeDescListPtr map_columns, ForeignKey::FKTYPE fk_type=ForeignKey::NOACTION,const std::string& viewname="", const std::string& alternativeType="", const int& colIndex=-1, const long& id=-1)
		: ObjectPropertyDesc(name,tablename,type,dimension,notNull,alternativeType, colIndex, id)
		, TableConstrainedAttributeDesc(map_columns,fk_type,viewname)
	{}	
	~ConstrainedObjectPropertyDesc(void)
	{

	}
	virtual bool operator ==(const ConstrainedObjectPropertyDesc& other) const
	{
		return (ObjectPropertyDesc::operator==(other) && TableConstrainedAttributeDesc::operator==(other));
	}
	virtual bool isConstrained() const;

private:
	template <typename Archive> 
	void serialize(Archive &ar, const unsigned int version) 
	{ 
		//If you change members here do not forget to increment object version (see bottom of this file)
		ar & boost::serialization::make_nvp("ObjectPropertyDesc", boost::serialization::base_object<ObjectPropertyDesc>(*this) ); 
		ar & boost::serialization::make_nvp("ConstrainedObjectProperty", boost::serialization::base_object<TableConstrainedAttributeDesc>(*this) ); 
	}
};	  
typedef boost::shared_ptr<ConstrainedObjectPropertyDesc> ConstrainedObjectPropertyDescPtr;



}// namespace ClientUtils
}// namespace GST

BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::ForeignKeyItem);
BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::ForeignKey);
BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::TableConstrainedAttributeDesc);
BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::ConstrainedSimplexPropertyDesc);
BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::ConstrainedObjectPropertyDesc);

BOOST_CLASS_VERSION(GST::ClientUtils::ForeignKeyItem, 1)
BOOST_CLASS_VERSION(GST::ClientUtils::ForeignKey, 1)
BOOST_CLASS_VERSION(GST::ClientUtils::TableConstrainedAttributeDesc, 3)
BOOST_CLASS_VERSION(GST::ClientUtils::ConstrainedSimplexPropertyDesc, 3)
BOOST_CLASS_VERSION(GST::ClientUtils::ConstrainedObjectPropertyDesc, 3)

#endif
